/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/*
 * DragBarPanel.java
 *
 * Created on May 11, 2009, 3:40:42 PM
 */
package DragBarPanelPackage;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Point;
import java.util.Vector;
import javax.swing.JButton;
import javax.swing.JLabel;

/**
 *
 * @author Evan
 */
public class DragBarPanel extends javax.swing.JPanel
{

  /** Creates new form DragBarPanel */
  public DragBarPanel()
  {
    initComponents();
    setupButtons(DEFAULT_NUMBER_OF_BUTTONS);
    add(bg);
    setupBGListener();
  }

  public DragBarPanel(int numButtons)
  {
    initComponents();
    setupButtons(numButtons);
    add(bg);
    setupBGListener();
  }

  /** This method is called from within the constructor to
   * initialize the form.
   * WARNING: Do NOT modify this code. The content of this method is
   * always regenerated by the Form Editor.
   */
  @SuppressWarnings("unchecked")
  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
  private void initComponents() {

    setLayout(null);
  }// </editor-fold>//GEN-END:initComponents
  // Variables declaration - do not modify//GEN-BEGIN:variables
  // End of variables declaration//GEN-END:variables

  // My variables
  private Vector<DragBarButton> theButtons = new Vector<DragBarButton>();
  private static final int DEFAULT_BUTTON_HEIGHT = 30;
  private static final String DEFAULT_BUTTON_NAME_ROOT = "";
  // The gap between buttons
  private static final int DEFAULT_GAP_SIZE = 20;
  // the gap between buttons and the left and right borders
  private static final int DEFAULT_EDGE_GAP_SIZE = 10;
  private int zeroHeight = 0;
  private DragBarBG bg = new DragBarBG();
  // scale indicates maximum absolute value of the bar numbers
  private double scale = 1.0;
  // this is value is how close to the border the buttons can get
  int borderBufferZone = 50;
  // number of decimal places for display purposes
  int numberOfDecimals = 3;
  // indicates how heigh the label is
  private int labelHeight = 20;
  // indicates how many pixels above the button the label sits
  private int labelGap = 12;
  private final int DEFAULT_NUMBER_OF_BUTTONS = 4;
  // default zoom dimensions
  private final int DEFAULT_ZOOM_WIDTH = 180;
  private final int DEFAULT_ZOOM_HEIGHT = 60;
  // this is the max a scale can ever be set
  private final int ABSOLUTE_SCALE = 1;
  // indicates whether or not we allow button locking
  private boolean buttonLockingEnabled = false;


  // My methods
  public void setButtonLocking(boolean buttonLocking)
  {
    buttonLockingEnabled = buttonLocking;
  }

  public boolean isButtonLockingEnabled()
  {
    return buttonLockingEnabled;
  }

  public Vector<DragBarButton> getButtons()
  {
    return theButtons;
  }

  public int getButtonCount()
  {
    return theButtons.size();
  }

  private void setupButtons(int numButtons)
  {
    // we divide by numButtons + 1 because we want the locations of the
    // centers of the buttons, not the length they should be
    Dimension size = getSize();
    int xIncrement = size.width / (numButtons + 1);
    int buttonWidth = size.height / numButtons;

    for (int i = 0; i < numButtons; i++)
    {
      String name = DEFAULT_BUTTON_NAME_ROOT + (i + 1);

      final DragBarButton currentB = new DragBarButton(name);
      currentB.setName(name);
      currentB.setDoubleBuffered(true);
      int x = xIncrement * (i + 1);
      // buttons are set along the center of the panel
      int y = HEIGHT / 2;
      zeroHeight = y;

      bg.addLine(new Point(x, y));
      currentB.setLocation(x, y);
      currentB.setSize(buttonWidth, currentB.getSize().height);
      currentB.setPreferredSize(new Dimension(buttonWidth, DEFAULT_BUTTON_HEIGHT));

      currentB.addMouseMotionListener(new java.awt.event.MouseMotionAdapter()
      {

        public void mouseDragged(java.awt.event.MouseEvent evt)
        {
          buttonMouseDragged(evt, currentB);
        }
      });

      currentB.addMouseListener(new java.awt.event.MouseAdapter()
      {

        public void mouseClicked(java.awt.event.MouseEvent evt)
        {
          buttonMouseClicked(evt, currentB);
        }

        public void mouseEntered(java.awt.event.MouseEvent evt)
        {
          buttonMouseEntered(evt, currentB);
        }

        public void mouseExited(java.awt.event.MouseEvent evt)
        {
          buttonMouseExited(evt, currentB);
        }

        public void mouseReleased(java.awt.event.MouseEvent evt)
        {
          buttonMouseReleased(evt, currentB);
        }
      });

      theButtons.add(currentB);
      add(currentB);
      add(currentB.getTheLabel());
    }
  }

  // returns the new button created
  public DragBarButton addNewButton(String name)
  {
    // setup the new button
    final DragBarButton newButton = new DragBarButton(name);
    newButton.setName(name);
    newButton.setDoubleBuffered(true);
    newButton.setLocation(new Point(0, HEIGHT / 2));
    newButton.addMouseMotionListener(new java.awt.event.MouseMotionAdapter()
    {

      public void mouseDragged(java.awt.event.MouseEvent evt)
      {
        buttonMouseDragged(evt, newButton);
      }
    });

    newButton.addMouseListener(new java.awt.event.MouseAdapter()
    {

      public void mouseClicked(java.awt.event.MouseEvent evt)
      {
        buttonMouseClicked(evt, newButton);
      }

      public void mouseEntered(java.awt.event.MouseEvent evt)
      {
        buttonMouseEntered(evt, newButton);
      }

      public void mouseExited(java.awt.event.MouseEvent evt)
      {
        buttonMouseExited(evt, newButton);
      }

      public void mouseReleased(java.awt.event.MouseEvent evt)
      {
        buttonMouseReleased(evt, newButton);
      }
    });
    theButtons.add(newButton);

    // clear the bg
    bg.clear();
    // clear container
    this.removeAll();

    // setup vertical lines
    // placeholder, since setSize calculates actual locations.
    for (int i = 0; i < theButtons.size(); i++)
    {
      add(theButtons.get(i));
      add(theButtons.get(i).getTheLabel());
      bg.addLine(new Point(0, 0));
    }
    add(bg);

    setSize(getSize());
    return newButton;
  }

  public void removeAButton(int buttonIndex)
  {
    if ((buttonIndex < theButtons.size()) && (buttonIndex > -1))
    {
      theButtons.remove(buttonIndex);
      // clear the bg
      bg.clear();
      // clear the container
      this.removeAll();

      // setup vertical lines
      // placeholder, since setSize calculates actual locations.
      for (int i = 0; i < theButtons.size(); i++)
      {
        add(theButtons.get(i));
        add(theButtons.get(i).getTheLabel());
        bg.addLine(new Point(0, 0));
      }
      add(bg);

      setSize(getSize());
    }
    else
    {
      System.out.println("cannot remove button.  bad index.");
    }
  }

  public void setSize(Dimension d)
  {
    int oldHeight = getSize().height;
    super.setSize(d);
    // we divide by numButtons + 1 because we want the locations of the
    // centers of the buttons, not the length they should be
    int numButtons = theButtons.size();
    Dimension size = getSize();
    zeroHeight = size.height / 2;
    int xIncrement = size.width / (numButtons + 1);
    int totalGapUsage = (numButtons - 1) * DEFAULT_GAP_SIZE + DEFAULT_EDGE_GAP_SIZE;

    // if the panel is too small to have gaps, then just ignore them.
    if (totalGapUsage >= size.width)
    {
      totalGapUsage = 0;
    }

    int buttonWidth = (size.width - totalGapUsage) / numButtons;
    DragBarButton.setDefaultSize(new Dimension(buttonWidth, DEFAULT_BUTTON_HEIGHT));

    for (int i = 0; i < numButtons; i++)
    {
      DragBarButton currentB = theButtons.get(i);
      int x = xIncrement * (i + 1);
      // buttons are set along the center of the panel
      int y = size.height / 2;

      bg.updateLineX(i, new Point(x, y));
      Point corner = getCorner(new Point(x, y), buttonWidth, DEFAULT_BUTTON_HEIGHT);

      currentB.setLocation(corner);
      currentB.setOldLocation(corner);
      currentB.setSize(buttonWidth, DEFAULT_BUTTON_HEIGHT);
      currentB.setPreferredSize(new Dimension(buttonWidth, DEFAULT_BUTTON_HEIGHT));
      currentB.setOldSize(new Dimension(buttonWidth, DEFAULT_BUTTON_HEIGHT));
      setupButtonLabel(currentB);
    }

    bg.setSize(getSize());
    int maxColorHeight = size.height / 2 - borderBufferZone;
    bg.setMaxColorHeight(maxColorHeight);
  }

  private void resetButtonLocations()
  {
    System.out.println("testing button resetting");
  }

  // returns a point representing the upper left corner of a button with
  // the input being the center of the button.
  private Point getCorner(Point center, int width, int height)
  {
    int xTranslate = width / 2;
    int yTranslate = height / 2;
    return new Point(center.x - xTranslate, center.y - yTranslate);
  }

  private void buttonMouseDragged(java.awt.event.MouseEvent evt, DragBarButton theButton)
  {
    if (theButton.isEnabled())
    {
      // the button should not have an arrow if it is moved on screen manually
      bg.setTriangleVector(getIndexOfButton(theButton), false);
      theButton.setIsPressed(true);
      //theButton.setSize(theButton.getOldSize());
      theButton.setSize(DragBarButton.getDefaultSize());
      theButton.setLocation(theButton.getOldLocation());
      //theButton.setIsExpanded(false);

      Point evtPt = evt.getPoint();
      Point oldPt = theButton.getOldLocation();
      //int buttonHeight = theButton.getOldSize().height;
      int buttonHeight = theButton.getDefaultSize().height;
      int halfHeight = buttonHeight / 2;

      int destinationX;
      if (theButton.isHorizontallyLocked())
      {
        destinationX = oldPt.x;
      }
      else
      {
        destinationX = oldPt.x + evtPt.x - ((theButton.getDefaultSize().width) / 2);
      }

      int destinationY = oldPt.y + evtPt.y - halfHeight;
      // this is the maximum distance the button can move from the center based on the border buffer
      double maxDistance = (getSize().height / 2.0) - borderBufferZone;
      // these are the upper and lower y values for the buttons at their maximums
      //double lowerMax = (getSize().height / 2.0) + maxDistance - (buttonHeight / 2);
      //double upperMax = (getSize().height / 2.0) - maxDistance - (buttonHeight / 2);
      int lowerMax = getSize().height - borderBufferZone - halfHeight;
      int upperMax = borderBufferZone - halfHeight;

      // if the destination is too close to the border, change it
      if (destinationY <= upperMax)
      {
        destinationY = upperMax;
      }
      else
      {
        if (destinationY >= lowerMax)
        {
          destinationY = lowerMax;
        }
      }

      // hValue is the height in relation to the zero height
      //double hValue = -(destinationY + (buttonHeight / 2.0) - zeroHeight);
      // scaledValue is the actual display value
      //double scaledValue = (hValue / (maxDistance)) * scale;

      int lineX = destinationX + (DragBarButton.getDefaultSize().width / 2);
      int lineY = destinationY + (DragBarButton.getDefaultSize().height / 2);
      // scaled value will be based on he center of the button
      double scaledValue = getValueFromHeight(lineY);
      theButton.setValue(scaledValue);
      double truncated = truncateDouble(scaledValue);
      Point destination = new Point(destinationX, destinationY);
      theButton.setOldLocation(destination);
      theButton.setLocation(destination);
      if (theButton.usingUserDefinedLabel())
      {
        theButton.getTheLabel().setText(theButton.getUserDefinedLabelText());
      }
      else
      {
        theButton.getTheLabel().setText("" + truncated);
      }
      setupButtonLabel(theButton);

      // find the index value for the vertical bars
      int index = 0;
      for (int i = 0; i < theButtons.size(); i++)
      {
        if (theButtons.get(i).equals(theButton))
        {
          index = i;
        }
      }

      bg.updateLineX(index, new Point(lineX, lineY));
      bg.drawLines();

      //theButton.setOldLocation(theButton.getLocation());
      theButton.setOldSize(theButton.getSize());
      //theButton.setIsExpanded(true);
      zoomSize(theButton);
      repaint();

      // for testing purposes
      //System.out.println("this is the destinationY: " + destinationY);
      double hv = (scaledValue / scale) * ((getSize().height / 2.0) - borderBufferZone);
      double destY = -(theButton.getHeight() / 2.0) + zeroHeight - hv;

    //System.out.println("this is the calculated Y: " + destY);
    }
  }

  // moves a single button to the provided value
  // THIS IS SOMEWHAT BUGGY... NEED TO FIX... BUT LOW PRIORITY!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  public void setButtonValueAndMove(DragBarButton b, double value)
  {
    // first check if the value is beyond the absolute scale
    if (value > ABSOLUTE_SCALE)
    {
      value = ABSOLUTE_SCALE;
    }
    else
    {
      if (value < -ABSOLUTE_SCALE)
      {
        value = -ABSOLUTE_SCALE;
      }
    }

    double scaledValue = value;
    if (value > scale)
    {
      scaledValue = scale;
      bg.setTriangleVector(getIndexOfButton(b), true);
    }
    else
    {
      if (value < -scale)
      {
        scaledValue = -scale;
        bg.setTriangleVector(getIndexOfButton(b), true);
      }
      else
      {
        bg.setTriangleVector(getIndexOfButton(b), false);
      }
    }

    // calculate the proper height based on this value
    // note that this is the height of the CENTER of the button
    int centerH = getHeightFromValue(scaledValue);
    // this here is fishy!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    int newH = centerH + b.getDefaultSize().height / 2;
    // CHECK THIS HERE... HOW DO WE MOVE BUTTONS AGAIN??????????????????????????????????????????
    b.setValue(value);
    double truncated = truncateDouble(value);
    Point destination = new Point(b.getLocation().x, newH);
    b.setOldLocation(new Point(b.getOldLocation().x, newH));
    b.setLocation(destination);
    if (b.usingUserDefinedLabel())
    {
      b.getTheLabel().setText(b.getUserDefinedLabelText());
    }
    else
    {
      b.getTheLabel().setText("" + truncated);
    }
    setupButtonLabel(b);

    int lineX = b.getOldLocation().x + (DragBarButton.getDefaultSize().width / 2);
    int lineY = destination.y + (DragBarButton.getDefaultSize().height / 2);

    // find the index value for the vertical bars
    int index = 0;
    for (int i = 0; i < theButtons.size(); i++)
    {
      if (theButtons.get(i).equals(b))
      {
        index = i;
      }
    }

    bg.updateLineX(index, new Point(lineX, lineY));
    bg.drawLines();

    b.setOldSize(b.getSize());
    repaint();
  }

  public void buttonMouseClicked(java.awt.event.MouseEvent evt, DragBarButton currentB)
  {
    // if they double click, toggle lock
    if ((evt.getClickCount() >= 2) && buttonLockingEnabled)
    {
      boolean lockedState = !currentB.isHorizontallyLocked();
      currentB.setHorizontallyLocked(lockedState);
      System.out.println("locked state: " + lockedState);

      if (lockedState)
      {
        currentB.setBackground(DragBarButton.HORIZONTAL_LOCK_COLOR);
      }
      else
      {
        currentB.setBackground(DragBarButton.DEFAULT_COLOR);
      }
    }
  }

  private void buttonMouseReleased(java.awt.event.MouseEvent evt, DragBarButton currentB)
  {
    if (currentB.isExpanded())
    {
      currentB.setIsPressed(false);
      currentB.setLocation(currentB.getOldLocation());
      //currentB.setSize(currentB.getOldSize());
      currentB.setSize(DragBarButton.getDefaultSize());
      currentB.setIsExpanded(false);
      // reset button label
      setupButtonLabel(currentB);
      repaint();
    }
  }

  private void buttonMouseEntered(java.awt.event.MouseEvent evt, DragBarButton currentB)
  {
    if (!currentB.isExpanded())
    {
      //System.out.println("value is: " + currentB.getValue());
      //System.out.println()
      currentB.setOldLocation(currentB.getLocation());
      currentB.setOldSize(currentB.getSize());
      //currentB.setIsExpanded(true);
      zoomSize(currentB);
      repaint();
    }
  }

  private void buttonMouseExited(java.awt.event.MouseEvent evt, DragBarButton currentB)
  {
    if (currentB.isExpanded())//&& !currentB.isPressed())
    {
      currentB.setLocation(currentB.getOldLocation());
      //System.out.println("the old location: " + currentB.getOldLocation().toString());
      //currentB.setSize(currentB.getOldSize());
      currentB.setSize(DragBarButton.getDefaultSize());
      currentB.setIsExpanded(false);
      // reset button label
      setupButtonLabel(currentB);
      repaint();
    }
  }

  public double calculateScaledValue(JButton theButton)
  {
    // this is the maximum distance the button can move from the center based on the border buffer
    double maxDistance = (getSize().height / 2.0) - borderBufferZone;
    Point p = theButton.getLocation();
    double hValue = -(p.y + (theButton.getHeight() / 2.0) - zeroHeight);
    double retValue = (hValue / (maxDistance)) * scale;
    // This is to get rid of the stupid -0.0 display problem
    if (retValue == 0)
    {
      retValue = 0;
    }
    return retValue;
  }

  // grows the current button and also grows the label
  public void zoomSize(DragBarButton currentB)
  {
    //double widthZoomFactor = 2;
    //double heightZoomFactor = 1.2;
    Dimension d = currentB.getSize();
    //Dimension newD = new Dimension((int) (d.width * widthZoomFactor), (int) (d.height * heightZoomFactor));
    Dimension newD = new Dimension(DEFAULT_ZOOM_WIDTH, DEFAULT_ZOOM_HEIGHT);
    int newX = currentB.getLocation().x - ((newD.width - d.width) / 2);
    int newY = currentB.getLocation().y - ((newD.height - d.height) / 2);
    Point newPt = new Point(newX, newY);

    currentB.setSize(newD);
    currentB.setLocation(newPt);

    // setup the button label too
    zoomButtonLabel(currentB);

    this.setComponentZOrder(currentB, 0);
    currentB.setIsExpanded(true);
  }

  public void clearBG()
  {
    bg.clear();
  }

  public void reset()
  {
    // FIX THIS... INADVERTANTLY RESETS BUTTONS TO MIDDLE
    setSize(getSize());
    bg.drawLines();
    for (int i = 0; i < theButtons.size(); i++)
    {
      DragBarButton b = theButtons.get(i);
      b.setValue(0);
      setupButtonLabel(b);
      b.repaint();
    }
  }

  // truncates the double for printing purposes
  private double truncateDouble(double input)
  {
    return (double) (((int) (input * Math.pow(10, numberOfDecimals))) / Math.pow(10, numberOfDecimals));
  }

  public void setScale(double newScale)
  {
    if (newScale <= ABSOLUTE_SCALE)
    {
      scale = newScale;
    }
  }

  public double getScale()
  {
    return scale;
  }

  // simply positions the label properly given an index
  public void setupButtonLabel(DragBarButton b)
  {
    JLabel l = b.getTheLabel();

    int labelWidth = b.getSize().width;
    Dimension newSize = new Dimension(labelWidth, labelHeight);

    int labelX = b.getLocation().x;
    int labelY;

    if (b.getValue() >= 0)
    {
      labelY = b.getLocation().y - (labelHeight + labelGap);
    }
    else
    {
      labelY = b.getLocation().y + b.getSize().height + labelGap;
    }

    l.setSize(newSize);
    l.setLocation(labelX, labelY);
    l.setForeground(Color.black);
    if (b.usingUserDefinedLabel())
    {
      b.getTheLabel().setText(b.getUserDefinedLabelText());
    }
    else
    {
      b.getTheLabel().setText("" + truncateDouble(b.getValue()));
    }
  //l.setText("" + truncateDouble(b.getValue()));
  }

  // simply makes the button larger for zooming... does not change location
  public void zoomButtonLabel(DragBarButton b)
  {
    JLabel l = b.getTheLabel();

    int labelWidth = b.getSize().width;
    Dimension newSize = new Dimension(labelWidth, labelHeight);

    l.setSize(newSize);
    if (b.usingUserDefinedLabel())
    {
      l.setForeground(Color.red);
      b.getTheLabel().setText(b.getUserDefinedLabelText());
    }
    else
    {
      b.getTheLabel().setText("" + truncateDouble(b.getValue()));
    }
    //l.setText("" + truncateDouble(b.getValue()));
  }

  // makes the panel refresh if the user click the bg
  private void setupBGListener()
  {
    bg.addMouseListener(new java.awt.event.MouseAdapter()
    {

      public void mouseClicked(java.awt.event.MouseEvent evt)
      {
        repaint();
      }

      public void mouseEntered(java.awt.event.MouseEvent evt)
      {
        repaint();
      }
    });
  }

  // set all the buttons to the provided values
  public void setButtonValues(Vector<Double> newValues)
  {
    if (newValues.size() == theButtons.size())
    {
      for (int i = 0; i < newValues.size(); i++)
      {
        setButtonValueAndMove(theButtons.get(i), newValues.get(i));
      }
    }
    else
    {
      System.out.println("ERROR IN SETTING BUTTON VALUES: size mismatch");
    }
  }

  // simply returns where the button should be given this value.  Note:
  // returns the BUTTON'S height, so it's based on the upper left corner
  public int getHeightFromValue(double value)
  {
    int ret = 0;

    // this is the percent of the max distance
    double percentH = value / scale;

    Dimension size = getSize();
    zeroHeight = size.height / 2;

    int maxVertDistance = zeroHeight - borderBufferZone;
    int distanceFromCenter = (int) (percentH * maxVertDistance);

    // since down is + y on screen, we flip signs
    ret = zeroHeight - distanceFromCenter - theButtons.get(0).getSize().height / 2;

    return ret;
  }

  public double getValueFromHeight(int height)
  {
    double ret = 0;

    double maxVertDistance = zeroHeight - borderBufferZone;
    // this is the distance from the center... it will be negative if the
    // height is below (greater than) the zeroheight
    double distanceFromCenter = zeroHeight - height;
    ret = distanceFromCenter / maxVertDistance * scale;

    return ret;
  }

  public int getIndexOfButton(DragBarButton b)
  {
    int ret = -1;
    for (int i = 0; i < theButtons.size(); i++)
    {
      if (theButtons.get(i).equals(b))
      {
        return i;
      }
    }
    return ret;
  }
}
